using System;
using System.Collections.Generic;
using System.Text;

using System.IO;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Net;
using System.Xml.Xsl;
using System.Reflection;
using System.Xml;
using System.ComponentModel;
using System.Data;

namespace PhilWicklund.com.RSSReaderWebPart
{
    public class RSSReaderWebPart :
      System.Web.UI.WebControls.WebParts.WebPart,
		ICallbackEventHandler
    {

		#region Properties
		private int numberOfPostsToDisplay = 5;
		private string xmlUrl;
		[Personalizable(PersonalizationScope.Shared),
		WebBrowsable(false),
		WebDisplayName("Feed Url"),
		WebDescription("Set your RSS feed's XML URL here!"),
		Category("Configuration")]
		public string XmlUrl
		{
			get { return xmlUrl; }
			set { xmlUrl = value; }
		}

		public enum RenderMode
		{
			Full,
			Titles
		}
		private RenderMode headlineMode = RenderMode.Full;
		[Personalizable(PersonalizationScope.User),
		WebBrowsable(false),
		WebDisplayName("Headline Mode"),
		WebDescription("Do you only want to display headlines?"),
		Category("Configuration")]
		public RenderMode HeadlineMode
		{
			get { return headlineMode; }
			set { headlineMode = value; }
		}
		#endregion

		//private int r = new Random();
		private string rssSpanId = null;
		private Panel div;
		private Stream xmlResponseStream = null;
		private WebRequest xmlReq;
		const string GEARS_GIF = "/_layouts/images/happenings/crperspc.gif";
		const string BLANK_GIF = "/_layouts/images/BLANK.GIF";

		protected override void OnInit(EventArgs e)
		{
			rssSpanId = "rss" + this.ID;
			base.OnInit(e);
		}

		//OnLoad places the JavaScript required for the AJAX-style
		//  framework onto the page
		protected override void OnLoad(EventArgs e)
		{
			System.Web.UI.ClientScriptManager csm = Page.ClientScript;
			StringBuilder js = new StringBuilder();

			//General JavaScript functions that can be added by any Web Part
			string SCRIPT_NAME = "GeneralClientCallbackScript" + rssSpanId;

			//CallServer will invoke RaiseCallbackEvent server-side out of band
			//  The JavaScript function does not have to be called CallServer
			//.NET will generate a JavaScript WebForm_DoCallback function call
			//  when GetCallbackEventReference is called which wires up all
			//  the callbacks
			//arg will determine which case of the switch
			//  statement in GetCallbackResult will run
			//context needs to be the id of the client-side element
			//  to update with the return string
			js.Append("function CallServerRSS" + rssSpanId + "(arg, context)")
			  .Append("{ \n")
			  .Append("  try \n")
			  .Append("  { \n")
			  .Append("    var element = document.getElementById(context); \n")
			  .Append("    if(element) \n")
			  .Append("    { \n")
				//.Append("      if(arg != 'Initial') \n")
				//.Append("      { \n")
			  .Append("        element.innerHTML = '<img src=\\\'" + GEARS_GIF + "\\\' alt=\\\'Refreshing...\\\' />'; \n")
				//.Append("      } \n")
			  .Append(csm.GetCallbackEventReference(
							 this,
							 "arg",
							 "HandleCallbackResult" + rssSpanId,
							 "context",
							 true)
					 )
			  .Append("    } \n")
			  .Append("  } \n")
			  .Append("  catch(e){window.status = 'ERROR:' + e.Message} \n")
			  .Append("} \n");

			//HandleCallbackResult will catch the string returned
			//  The JavaScript function does not have to be called HandleCallbackResult
			//arg will contain the return string from GetCallbackResult
			//context will be the id of the client-side element
			//  to update with the return string
			js.Append("function HandleCallbackResult" + rssSpanId + "(arg, context) \n")
			  .Append("{ \n")
			  .Append("  try \n")
			  .Append("  { \n")
			  .Append("    var element = \n")
			  .Append("      document.getElementById(context); \n")
			  .Append("    if(element) \n")
			  .Append("    { \n")
			  .Append("      element.innerHTML = arg; \n")
			  .Append("    } \n")
			  .Append("  } \n")
			  .Append("  catch(e){window.status = 'ERROR:' + e.Message} \n")
			  .Append("} \n");

			if (!csm.IsClientScriptBlockRegistered(SCRIPT_NAME))
			{
				csm.RegisterClientScriptBlock(
					this.GetType(),
					SCRIPT_NAME,
					js.ToString(),
					true);
			}

			//Clear the string builder
			js.Remove(0, js.Length);

			//BootWebPart is needed because functions that run when
			//Body Onload is fired cannot have parameters
			//Invoke CallServer with a call to populate this Web Part's innerHTML
			js.Append("function BootWebPartRSS" + rssSpanId + "() \n")
			  .Append("{ \n")
			  .Append("  CallServerRSS" + rssSpanId + "('Initial', '" + rssSpanId + "'); \n")
			  .Append("} \n");

			//Run BootWebPart when the Page has fully loaded
			js.Append("_spBodyOnLoadFunctionNames.push('BootWebPartRSS" + rssSpanId + "'); \n");

			//Unique JavaScript name for this Web Part script
			SCRIPT_NAME = this.ID + "Script";

			csm.RegisterClientScriptBlock(
				this.GetType(),
				SCRIPT_NAME,
				js.ToString(),
				true);
		}

		protected override void CreateChildControls()
		{
			try
			{
				Panel div = new Panel();
				div.Attributes.Add("id", rssSpanId);

				Image img = new Image();
				img.ImageUrl = GEARS_GIF;
				div.Controls.Add(img);
				this.Controls.Add(div);
			}
			catch (Exception ex)
			{
				this.Controls.Add(new LiteralControl(ex.Message));
			}
		}

		//public override WebPartVerbCollection Verbs
		//{
		//    get
		//    {
		//        WebPartVerb verb = new WebPartVerb(
		//            "RssFeedRefresh_ClientSide",
		//            "javascript:CallServerRSS('Initial', '" 
		//                + div.ClientID
		//                + "'); return false;"
		//            );
		//        verb.Text = "Refresh RSS Feed";
		//        verb.Description = "Click here to refresh the feed with the latest data.";
		//        verb.ImageUrl = "/_layouts/images/recurrence.gif";

		//        WebPartVerb[] newVerbs = new WebPartVerb[]
		//        {
		//            verb
		//        };
		//        WebPartVerbCollection allVerbs = new WebPartVerbCollection(base.Verbs, newVerbs);
		//        return allVerbs;
		//    }
		//}

		public override EditorPartCollection CreateEditorParts()
		{
			List<EditorPart> editorParts = new List<EditorPart>(1);
			EditorPart part = new RSSReaderEP();
			part.ID = this.ID + "_rssViewerEditor";
			editorParts.Add(part);
			EditorPartCollection baseparts = base.CreateEditorParts();
			return new EditorPartCollection(baseparts, editorParts);
		}

		//This is the server control code that would usually
		//  get placed into CreateChildControls
		//All the user interface controls should be rendered
		//  the the writer
		private HtmlTextWriter RenderUI(HtmlTextWriter writer)
		{
			///Label span = new Label
			ImageButton refreshButton = null;

			div = AddRSS();
			div.Controls.Add(new LiteralControl("<br /><table border=\"0\"><tr><td valign='middle'>"));

			//Button that calls for the server date asynchronously and out of band.
			//The dateSpan id is used to get the element that holds the return string.
			refreshButton = new ImageButton();
			refreshButton.OnClientClick =
			  "javascript:CallServerRSS" + rssSpanId + "('Initial', '" + rssSpanId + "');";
			refreshButton.AlternateText = "Refresh Data";
			refreshButton.ImageUrl = "/_layouts/images/recurrence.gif";
			div.Controls.Add(refreshButton);
			div.Controls.Add(new LiteralControl("</td><td valign='middle'>Refresh Data</td></tr></table>"));

			div.RenderControl(writer);

			return writer;
		}
		protected override void RenderContents(HtmlTextWriter writer)
		{
			base.RenderContents(writer);
		}
		private Panel AddRSS()
		{
			Panel div = new Panel();
			if (string.IsNullOrEmpty(this.xmlUrl))
			{
				div.Controls.Add(new LiteralControl("Specify the feed please"));
				return div;
			}
			if (this.WebPartManager.DisplayMode.AllowPageDesign)
			{
				div.Controls.Add(new LiteralControl("No display while in design mode."));
				return div;
			}

			try
			{
				Uri xmlUri = new Uri(this.xmlUrl);
				xmlReq = WebRequest.CreateDefault(xmlUri);
				xmlReq.Credentials = CredentialCache.DefaultCredentials;
				xmlReq.Timeout = 10000; // 10 second timeout
				WebResponse response = xmlReq.GetResponse();
				this.xmlResponseStream = response.GetResponseStream();

				XslCompiledTransform transform = new XslCompiledTransform();
				string xslt;
				if (this.HeadlineMode == RenderMode.Full)
					xslt = @"Resources.RSS.xslt";
				else
					xslt = @"Resources.RssTitle.xslt";

				string resourceName = @"PhilWicklund.com.RSSReaderWebPart." + xslt;
				using (Stream res =
					Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
				{
					using (XmlTextReader stylesheet = new XmlTextReader(res))
					{
						transform.Load(stylesheet);
					}
				}

				StringWriter sWriter = new StringWriter();
				HtmlTextWriter writer = new HtmlTextWriter(sWriter);
				// Convert the streamed reponse to the requesting
				using (XmlReader reader = new XmlTextReader(this.xmlResponseStream))
				{
					XmlTextWriter results = new XmlTextWriter(writer.InnerWriter);
					//XmlDocument doc = new XmlDocument();
					//doc.Load(reader);
					//doc.WriteContentTo(results);
					transform.Transform(reader, results);
					reader.Close();
				}

				div.Controls.Add(new LiteralControl(sWriter.ToString()));
			}
			catch (System.Security.SecurityException)
			{
				div.Controls.Add(new LiteralControl("Permission Denied, this webpart requires a higher trust in the web configuration file."));
			}
			catch (Exception e)
			{
				div.Controls.Add(new LiteralControl(e.Message));
				if (this.xmlResponseStream != null)
				{
					this.xmlResponseStream.Close();
					this.xmlResponseStream.Dispose();
				}
			}
			return div;
		}

		#region ICallbackEventHandler Members

		//Holds the case of the switch statement requested
		private string _eventArgument = "";

		//Initially called by the client-side JavaScript
		public void RaiseCallbackEvent(string eventArgument)
		{
			//Simulate the call taking some time to return
			//System.Threading.Thread.Sleep(500);

			//Save aside the function requested
			_eventArgument = eventArgument;
		}

		//Called immediately after RaiseCallbackEvent
		//Generates the string to return to the client
		public string GetCallbackResult()
		{
			StringWriter sWriter = new StringWriter();
			HtmlTextWriter writer = new HtmlTextWriter(sWriter);
			//StringBuilder sb = new StringBuilder();

			Label lbl = null;

			switch (_eventArgument)
			{
				//Called when the page first loads to generate the 
				case "Initial":
					writer = RenderUI(writer);
					break;

				//Called when the Get Server Date button is clicked
				case "ShowSpinny":
					Image img = new Image();
					img.ImageUrl = GEARS_GIF;
					img.RenderControl(writer);
					break;

				//Never called, this is an exception
				default:
					lbl = new Label();
					lbl.Text = "ERROR: Unknown eventArgument";
					lbl.ForeColor = System.Drawing.Color.Red;
					lbl.RenderControl(writer);
					break;
			}

			return sWriter.ToString();
		}
		#endregion

    }
}